import java.util.HashSet;
import java.util.Set;

//leetcode_141:环形链表
//给定一个链表，判断链表中是否有环。
//如果链表中有某个节点，可以通过连续跟踪 next 指针再次到达，则链表中存在环。
//为了表示给定链表中的环，我们使用整数 pos 来表示链表尾连接到链表中的位置（索引从 0 开始）。
//如果 pos 是 -1，则在该链表中没有环。注意：pos 不作为参数进行传递，仅仅是为了标识链表的实际情况。
//如果链表中存在环，则返回 true 。 否则，返回 false 。
public class leetcode_141 {
    //方法一：哈希表
//    最容易想到的方法是遍历所有节点，每次遍历到一个节点时，判断该节点此前是否被访问过。
//    具体地，我们可以使用哈希表来存储所有已经访问过的节点。每次我们到达一个节点，
//    如果该节点已经存在于哈希表中，则说明该链表是环形链表，否则就将该节点加入哈希表中。
//    重复这一过程，直到我们遍历完整个链表即可。
//    public boolean hasCycle(ListNode head) {
//        Set<ListNode> seen = new HashSet<ListNode>();
//        while (head != null) {
//            if (!seen.add(head)) {
//                return true;
//            }
//            head = head.next;
//        }
//        return false;
//    }
//    复杂度分析
//    时间复杂度：O(N)，其中N是链表中的节点数。最坏情况下我们需要遍历每个节点一次。
//    空间复杂度：O(N)，其中N是链表中的节点数。主要为哈希表的开销，
//    最坏情况下我们需要将每个节点插入到哈希表中一次。
    //方法二：快慢指针
//我们定义两个指针，一快一满。慢指针每次只移动一步，而快指针每次移动两步。初始时，慢指针在位置 head
//而快指针在位置 head.next。这样一来，如果在移动的过程中，快指针反过来追上慢指针，
//就说明该链表为环形链表。否则快指针将到达链表尾部，该链表不为环形链表。
public boolean hasCycle1(ListNode head) {
    if (head == null || head.next == null) {
        return false;
    }
    ListNode slow = head;
    ListNode fast = head.next;
    while (slow != fast) {
        if (fast == null || fast.next == null) {
            return false;
        }
        slow = slow.next;
        fast = fast.next.next;
    }
    return true;
}
//    复杂度分析
//    时间复杂度：O(N)，其中N是链表中的节点数。
//    空间复杂度：O(1)。我们只使用了两个指针的额外空间。
class ListNode {
    int val;
    ListNode next;

    ListNode(int x) {
        val = x;
        next = null;
    }
}

}
